#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>

// 排序比较函数
int cmp(const void *p, const void *q);
// 将牌中的字母变为大写
void upper_hand(char *in_hand);
// 将牌分为用#分隔的等级和花色两部分，并对两部分分别排序
void sort_hand(char *in_hand);
// 牌型判断
int classify_poker(const char *in_hand);

// 测试/驱动
int main()
{
    int i, ret;
    char in_hand[][12] = {"2s5s4s3s6s", "8cas7cad3h", "6s2d9c4hts",
                          "asqsjsksts", "asqcjdkhts", "1s5sjsksts"
                         };
    char *poker_type[] = {"Straight flush", "Four of a kind", "Full house",
                          "Flush", "Straight", "Three of a kind",
                          "Two pair", "One pair", "High card"};

    for(i = 0; i < sizeof(in_hand) / sizeof(in_hand[0]); i++)
    {
        upper_hand(in_hand[i]);
        sort_hand(in_hand[i]);
        ret = classify_poker(in_hand[i]);
        puts(poker_type[ret]);
    }

    return 0;
}

// 牌型判断
// 要求等级与花色用#分隔且要求等级与花色分别进行了排序
int classify_poker(const char *in_hand)
{
    // 正则缓存
    regex_t pat_buf;
    char err_buf[256];
    int c;
    int i;

    // 正则表达式数组
    char *pat[] = {
      "(2345A|23456|34567|45678|56789|6789T|"
      "789JT|89JQT|9JKQT|AJKQT)#(.)\\2{4}.*", // #0 同花顺
      "(.)\\1{3}.*#.*", // #1 四张
      "((.)\\2{2}(.)\\3{1}#.*|(.)\\4{1}(.)\\5{2}#.*)", // #2 葫芦(三张加一对)
      ".*#(.)\\1{4}.*", // #3 同花
      "(2345A|23456|34567|45678|56789|6789T|"
      "789JT|89JQT|9JKQT|AJKQT)#.*", // #4 顺子
      "(.)\\1{2}.*#.*", // #5 三张
      "(.)\\1{1}.*(.)\\2{1}.*#.*", // #6 两对
      "(.)\\1{1}.*#.*" // #7 一对
    };

    for(i = 0; i < 8; i++)
    {
        // 编译正则表达式
        c  = regcomp(&pat_buf, pat[i], REG_EXTENDED);
        if(c != 0)
        {
            regerror(c, &pat_buf, err_buf, sizeof(err_buf));
            err_buf[sizeof(err_buf) - 1] = '\0';
            printf("%s\n", err_buf);
            exit(EXIT_FAILURE);
        }

        // 用编译后的正则表达式实现查找
        c = regexec(&pat_buf, in_hand, 0, NULL, 0);
        // 释放pattern buffer
        regfree(&pat_buf);

        if(c == 0)
        {
            // 匹配成功，返回类型码
            return i;
        }
    }

    // 未匹配成功，返回其它类型码
    return i;
}

// 将牌用#分为等级和花色两部分，并对两部分进行排序
void sort_hand(char *s)
{
    char face[6] = {0};
    char suit[6] = {0};
    int i = 0;
    char *p = s;

    while(*p)
    {
        if(i % 2 == 0)
        {
            face[i / 2] = *p;
        }
        else
        {
            suit[i / 2] = *p;
        }
        i++;
        p++;
    }

    qsort(face, strlen(face), 1, cmp);
    qsort(suit, strlen(suit), 1, cmp);
    strcpy(s, face);
    strcat(s, "#");
    strcat(s, suit);
}

// 将字符串中的小写字母变换为大写，其它字符不变
void upper_hand(char *s)
{
    while(*s)
    {
        if(*s >= 'a' && *s <= 'z')
        {
            *s ^= 0x20; // 转换为大写
        }
        s++;
    }
}

// 排序比较函数
int cmp(const void *p, const void *q)
{
    char ch_p = *((char*)p);
    char ch_q = *((char*)q);

    return (ch_p > ch_q) - (ch_p < ch_q);
}
